package ee.android.kinokavad.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.http.AndroidHttpClient;
import android.provider.Settings;
import android.text.Html;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;

import ee.android.kinokavad.App;
import ee.android.kinokavad.R;
import ee.android.kinokavad.data.Cinema;
import ee.android.kinokavad.data.Movie;
import ee.android.kinokavad.data.Prefs;
import ee.android.kinokavad.data.Session;

public class NetworkUtils {

	private static final String TAG = NetworkUtils.class.getSimpleName();
	private static final String SOAP_CINEMAS = "http://www.microsoft.com/testwebservices123/getCinemasList";
	private static final String METHOD_CINEMAS = "getCinemasList";
	private static final String SOAP_SESSIONS = "http://www.microsoft.com/testwebservices123/getSessions";
	private static final String METHOD_SESSIONS = "getSessions";
	private static final String SOAP_MOVIES = "http://www.microsoft.com/testwebservices123/getAllMovieswithIds";
	private static final String METHOD_MOVIES = "getAllMovieswithIds";
	private static final String NAMESPACE = "http://www.microsoft.com/testwebservices123/";
	private static final String URL = "http://sandstorm.cs.ut.ee/MA/silver91/WebService1.asmx?WSDL";

	private static final String IMDB_ID_PATTERN = "[t][t](\\d+,?)+";

	/**
	 * Download all available cinemas from webservice.
	 * @return List
	 */
	public static List<Cinema> downloadAllCinemas(Context context){

		List<Cinema> cinemas = new ArrayList<Cinema>();

		List<Cinema> preparedCinemas = generateCinemas(context);
		try{

			SoapObject request = new SoapObject(NAMESPACE, METHOD_CINEMAS);

			SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

			envelope.dotNet = true;
			envelope.setOutputSoapObject(request);

			HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

			androidHttpTransport.call(SOAP_CINEMAS, envelope);

			SoapObject result = (SoapObject)envelope.getResponse();
			for (int i = 0; i < result.getPropertyCount(); i++){
				SoapObject object = (SoapObject) result.getProperty(i);
				String name = object.getProperty("Name").toString();
				String city = object.getProperty("City").toString();
				int id = Integer.parseInt(object.getProperty("id").toString());
				Cinema cinema = new Cinema(id, id, name, city);
				SharedPreferences settings = context.getSharedPreferences(Prefs.PREFS, 0);
				int cinemaIndex = settings.getInt("cinemaIndex", 0);
				for (Cinema prepared : preparedCinemas) {
					if (prepared.getName().equals(cinema.getName())) {
						cinema = prepared;
						prepared.setNetId(id);
						break;
					}
				}
				if (i==cinemaIndex) {
					cinema.setSelected(true);
				}
				cinemas.add(cinema);

			}

		} catch (Exception e){
			Log.e("ERROR", Log.getStackTraceString(e));
		}

		return cinemas;
	}

	private static List<Cinema> generateCinemas(Context context) {

		Gson gson = new Gson();
		List<Cinema> cinemas = new ArrayList<Cinema>();
		try {
			Resources res = context.getResources();
			InputStream inStream = res.openRawResource(R.raw.cinema_json);
			BufferedReader in = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
			String readLine;
			StringBuffer sb = new StringBuffer();
			while ((readLine = in.readLine()) != null) {
				sb.append(readLine);
			}
			cinemas = gson.fromJson(sb.toString(), new TypeToken<List<Cinema>>() {}.getType());
			inStream.close();
		} catch (Exception e) {
			Log.e("ERROR", Log.getStackTraceString(e));
		}
		return cinemas;
	}

	/**
	 * Download all sessions from web service for given date + 4 next days.
	 * @param year
	 * @param month
	 * @param day
	 * @return List
	 */
	public static List<Session> downloadAndSaveAllSessionsAndMovies(Context context, DateTime start, DateTime end){
		List<Session> sessions = new ArrayList<Session>();
		final App kinokavad = (App) context.getApplicationContext();
		List<Movie> movies = new ArrayList<Movie>();
		try {
			SoapObject request = new SoapObject(NAMESPACE, METHOD_SESSIONS);

			SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

			request.addProperty("startDate", start.toString("yyyy-MM-dd"));
			request.addProperty("endDate", end.toString("yyyy-MM-dd"));

			envelope.dotNet = true;
			envelope.setOutputSoapObject(request);

			HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

			androidHttpTransport.call(SOAP_SESSIONS, envelope);

			SoapObject result = (SoapObject)envelope.getResponse();
			kinokavad.getDataController().begin();
			for (int i = 0; i < result.getPropertyCount(); i++){
				SoapObject object = (SoapObject) result.getProperty(i);
				String date = object.getProperty("date").toString();
				String time = object.getProperty("time").toString();
				String dateTime = createDateTime(date, time);
				String pictureLink = object.getProperty("pictureLink").toString();
				int cinemaId = Integer.parseInt(object.getProperty("cinemaId").toString());
				int movieId = Integer.parseInt(object.getProperty("movieId").toString());
				Session session = new Session(cinemaId, movieId, dateTime, pictureLink);
				sessions.add(session);
				kinokavad.getDataController().addSessionToDatabase(session);
			}

			if (sessions.size() != 0){

				String ids = generateMovieIdList(sessions, kinokavad.getDataController().loadAllMoviesIdsFromDatabase());
				if (ids != null) {
					movies = downloadAllMovies(ids, sessions);
					for (Movie movie : movies) {
						kinokavad.getDataController().addMovieToDatabase(movie);
					}
				}
				kinokavad.getDataController().success();
			}

		} catch (Exception e){
			Log.e("ERROR", Log.getStackTraceString(e));
		} finally {
			kinokavad.getDataController().end();
		}

		addOmdbDataToMovies(kinokavad, movies);

		return sessions;
	}

	private static void addOmdbDataToMovies(final App kinokavad, final List<Movie> movies) {
		if (movies.size() != 0) {
			new Thread(new Runnable() {

				@Override
				public void run() {
					kinokavad.getDataController().open();
					Pattern pattern = Pattern.compile(IMDB_ID_PATTERN);;
					Matcher matcher;
					try {
						for (Movie movie : movies) {
							if (movie.getImdbLink() != null) {
								matcher = pattern.matcher(movie.getImdbLink());
								if (matcher.find()) {
									String imdbId = matcher.group();
									Movie omdbMovie = downloadOMDBdata(imdbId);
									if (omdbMovie != null) {
										omdbMovie.setNetId(movie.getNetId());
										if (App.LOCAL_LOG)
											Log.d(TAG, "OMDBMOVIE: " + omdbMovie.toLongString());
										String poster = omdbMovie.getPoster();
										if (!poster.startsWith("http://")) {
											poster = "http://"+poster;
										}
										omdbMovie.setPoster(poster);
										kinokavad.getDataController().updateMovie(omdbMovie);
									}
								}
							}
						}
						//						kinokavad.getDataController().success();
					} catch (Exception e) {
						Log.e("ERROR", Log.getStackTraceString(e));
					} finally {
						kinokavad.getDataController().closeRequest();
					}
				}
			}).start();
		}
	}

	private static String createDateTime(String date, String time) {
		time = time.substring(0, 5).replace(" ", "");
		date = date.substring(0, 10).replace(" ", "");
		String dateTime = DateTime.parse(date+time, DateTimeFormat.forPattern("dd.MM.yyyyHH:mm")).toString("yyyy-MM-dd'T'HH:mm");

		return dateTime;
	}

	/**
	 * Download all movies from webservice with given ids. Is called from inside getAllSessionsForDay().
	 * @param ids
	 * @return List
	 */
	public static List<Movie> downloadAllMovies(String ids, List<Session> sessions){

		List<Movie> movies = new ArrayList<Movie>();

		try {
			SoapObject request = new SoapObject(NAMESPACE, METHOD_MOVIES);

			SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapSerializationEnvelope.VER11);

			request.addProperty("idsstring", ids);

			envelope.dotNet = true;
			envelope.setOutputSoapObject(request);

			HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

			androidHttpTransport.call(SOAP_MOVIES, envelope);

			SoapObject result = (SoapObject)envelope.getResponse();

			for (int i = 0; i < result.getPropertyCount(); i++){
				SoapObject object = (SoapObject) result.getProperty(i);
				int id = Integer.parseInt(object.getProperty("id").toString());
				String title = object.getProperty("Title").toString();
				String originalTitle = object.getPropertyAsString("originalTitle");
				String description = object.getProperty("Description").toString();
				String genre = object.getProperty("Genre").toString();
				String youtubeLink = object.getProperty("youtubeLink").toString();
				String imdbLink = object.getProperty("imdbLink").toString();
				if (title.contains("anyType{}")){
					title = "";
				}
				if (description.contains("anyType{}")){
					description = "";
				}
				if (genre.contains("anyType{}")){
					genre = "";
				}
				if (youtubeLink.contains("anyType{}")){
					youtubeLink = "";
				} else if (!youtubeLink.startsWith("http://")) {
					youtubeLink = "http://"+youtubeLink;
				}
				if (imdbLink.contains("anyType{}")) {
					imdbLink = "";
				} else if (!imdbLink.startsWith("http://")) {
					imdbLink = "http://"+imdbLink;
				}

				Movie movie = new Movie(id, id, title, originalTitle, description, genre, youtubeLink, imdbLink, "");
				for (int j = 0; j < sessions.size(); j++) {
					if (sessions.get(j).getMovieId() == id){
						String picturelink = sessions.get(j).getPictureLink();
						if (! picturelink.contains("http://")){
							picturelink = "http://www.kavad.ee/picture/" + picturelink;
						}
						movie.setPictureLink(picturelink);
						break;
					} 
				}

				if (App.LOCAL_LOG)
					Log.d(TAG, "Movie: " + movie.getTitle());

				movies.add(movie);
			}

		} catch (Exception e){
			Log.e("ERROR", Log.getStackTraceString(e));
		}

		return movies;

	}

	public static String generateMovieIdList(List<Session> sessions, List<Long> loadedIds){

		if (App.LOCAL_LOG)
			Log.d(TAG, "Loaded ids: " + loadedIds);
		List<Long> checkList = loadedIds;
		StringBuilder sb = new StringBuilder();

		for (Session session : sessions) {
			if (!checkList.contains(session.getMovieId())) {
				checkList.add(session.getMovieId());
				sb.append(" " + session.getMovieId());
			}
		}
		if (sb.length() == 0) {
			return null;
		}
		String result = sb.substring(1);

		if (App.LOCAL_LOG)
			Log.d(TAG, "Downloading movies for ids: " + result);
		return result;

	}

	public static void showNoNetworkConnectionDialog(Context ctx1) {
		final Context ctx = ctx1;
		AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
		builder.setCancelable(true);
		builder.setMessage(R.string.no_connection);
		builder.setIcon(android.R.drawable.ic_dialog_alert);
		builder.setTitle(R.string.no_connection_title);
		builder.setPositiveButton("Seaded", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				ctx.startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
			}
		});
		builder.setNegativeButton("Tühista", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				return;
			}
		});
		builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			public void onCancel(DialogInterface dialog) {
				return;
			}
		});

		builder.show();
	}

	public static boolean isNetworkAvailable(Context context) {
		ConnectivityManager connectivityManager 
		= (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
		return activeNetworkInfo != null;
	}

	/**
	 * Sends GET request to API.
	 * @param URIString requested URI
	 * @return result in JSON format
	 */
	public static String getResultFromURI(String URIString) {
		String result = null;
		BufferedReader in = null;
		HttpClient client = new DefaultHttpClient();
		HttpGet request = new HttpGet(URIString);
		request.addHeader("Accept-Encoding", "gzip");
		try {
			HttpResponse response = client.execute(request);
			in = new BufferedReader (new InputStreamReader(AndroidHttpClient.getUngzippedContent(response.getEntity())));
			StringBuffer sb = new StringBuffer("");
			String line = "";

			while ((line = in.readLine()) != null) {
				sb.append(line);
			}
			in.close();
			result = Html.fromHtml(sb.toString()).toString();

		} catch (ClientProtocolException e) {
			Log.e("ERROR", Log.getStackTraceString(e));
		} catch (IOException e) {
			Log.e("ERROR", Log.getStackTraceString(e));

		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					Log.e("ERROR", Log.getStackTraceString(e));
				}
			}

		}
		return result;
	}

	public static Movie downloadOMDBdata(String imdbId) {
		try {
			String result = getResultFromURI("http://www.omdbapi.com/?i="+imdbId);
			Movie movie = new Gson().fromJson(result, Movie.class);
			if (movie.hasOmdbResponse()) {
				return movie;
			}
		} catch (NullPointerException e) {
			Log.e("ERROR", Log.getStackTraceString(e));
		} catch (JsonSyntaxException e) {
			Log.e("ERROR", Log.getStackTraceString(e));
		}
		return null;
	}




}
